/*
 * Students:
 * Massimiliano Rango - 610826
 * Martina Benedetto - 610962
 * 
 * A.A 2011/2012
 * 
 */ 

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import java.nio.ByteBuffer; 
import java.nio.ByteOrder;

/**
 *
 * This class represents a node which uses the RED protocol to detect if there is
 * a clone in the network.
 * 
 * @version 1.0 27 Dec 2011
 * @author Martina Benedetto 
 *
 */

public class REDNode extends Node {
	
	public REDNode(int ID, Position pos, Configuration conf) {
		super(ID, pos, conf);
	}
	
	/**
	 * 
	 * @param id is the id of the Node that sent the location claim
	 * @param rand is the value made known by the hypervisor at the beginning 
	 * of the simulation
	 * @param j is a counter value
	 * @return a Position whose coordinates are calculated with a pseudo 
	 * random function
	 */
	private Position pseudoRandom(int id, int rand, int j) {
		String s_id = Integer.toString(id);
		String s_rand = Integer.toString(rand);
		String s_result = s_id + s_rand;
		float x = hash(s_result, (j*2) + 1);
		float y = hash(s_result, (j*2) + 2);
		return new Position(x, y);
	}
	
	/**
	 * 
	 * @param s is the concatenation of the id of the Node and the value rand
	 * @param k is a counter value
	 * @return a coordinate for the pseudo random position
	 */
	private float hash(String s, int k) {
		byte[] text = s.getBytes();
		try {
			MessageDigest algorithm = MessageDigest.getInstance("MD5");
			for(int i = 0; i < k; i++){ 
				algorithm.update(text);
				byte messageDigest[] = algorithm.digest();
				text = messageDigest;
			}
		}
		catch (NoSuchAlgorithmException nsae) {
		}
		byte s_text[] = text;
		float c = ByteBuffer.wrap(s_text).order(ByteOrder.nativeOrder()).getFloat();
		while(Math.abs(c) > 1) { 	/* we must obtain a float x where 0 <= x <= 1 */
			c /= 10;
		}
		return Math.abs(c);
	}
	
	
	public void run() {
		try {
			for(int i = 0; !isKilled() && i < sizeNeighbours();) {
				if(sendMessage(new LocationClaim(getID(), getPosition()), getNeighbour(i), true) != null)
					i++;
			}
			m_conf.addNodeLocClaim();	/* this node has had a chance to send its location claims */
			if(!isKilled()) {
				synchronized(m_conf.lockEndSimulation) {
					while(m_conf.getNodeLocClaim() < m_conf.getNNodes() + 1) {
						m_conf.lockEndSimulation.wait();
					}
				}
				while(!isKilled() && !m_conf.getCloneFound() && m_conf.getCirculatingMess() > 0) {
					Message rec = null;
					synchronized(lockIncoming) {
						while(sizeIncoming() == 0 && m_conf.getCirculatingMess() > 0 && !m_conf.getCloneFound()) {
							lockIncoming.wait();
						}
					} /*end synch lockIncoming*/
					/* this node wakes up if another node sent him a message or the hypervisor discovered that there are no message circulating */
					if(m_conf.getCirculatingMess() > 0 && !m_conf.getCloneFound()) {
						rec = receiveMessage();
					}
					if(!isKilled() && rec != null) { /* this node has not died while receiving the message */
						if(rec instanceof LocationClaim) {
							Random r = new Random();
							float random = r.nextFloat();
							if(random < m_conf.getProb()) {
								for(int i = 0; !isKilled() && i < m_conf.getG(); i++) {
									Position finalDest = pseudoRandom(rec.getID(), m_conf.getRand(), i);
									ControlMessage cm = new ControlMessage(rec.getID(), rec.getPosition(), finalDest);
									addArchive(cm);
									if(!m_conf.getCloneFound() && !isKilled()) {
										Node nearest = nearest(finalDest);
										if(nearest == this) {
											cloneDetection(true);
										}
										while(!m_conf.getCloneFound() && !isKilled() && !sendToNearest(cm, nearest, true)) {
											nearest = nearest(finalDest);
											if(nearest == this) {
												cloneDetection(false);
											}
										}
									}
								}
							}
							m_conf.removeCirculatingMess(1); /* the incoming location claim has been processed */
						} else if(rec instanceof ControlMessage) {
							ControlMessage cm = (ControlMessage)rec;
							addArchive(cm);
							if(!m_conf.getCloneFound() && !isKilled()) {
								Node nearest = nearest(cm.getDestination());
								if(nearest == this) {
									cloneDetection(false);
								}
								while(!m_conf.getCloneFound() && !isKilled() && !sendToNearest(cm, nearest, false)) {
									nearest = nearest(cm.getDestination());
									if(nearest == this) {
										cloneDetection(false);
									}

								}
							}
						}
					}
				}
			}
		} catch(InterruptedException e) {
			die();
			m_conf.addKilledNode();
			return;
		}
		setKilled();
		m_conf.addKilledNode();
	}		
}
